DEMO: Visualizing waves#

import numpy as np  
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive
import scipy  
from scipy.constants import physical_constants, hbar, h, c, k, m_e, Rydberg, e, N_A

%matplotlib inline
%config InlineBackend.figure_format='retina'

Standing and traveling waves in 1D#

We begin by plotting a simple periodic function using numpy (np) and matplotlib.pyplot (plt)

\[y = \sin(kx)=\sin\left(\frac{2\pi}{\lambda} x\right)\]
L=0.3            # Try different wavelengths

x = np.linspace(0.0, 1.0, 1000)

y = np.sin(2 * np.pi * x/L)

plt.plot(x, y)
[<matplotlib.lines.Line2D at 0x1666eec40>]
../_images/b0a77726734e88abe4226d4a391119896a44732031854a5399152909288d3d32.png

By putting above example inside a python function will make wavelength exploration convenient

def wave(L=2):
    
    x = np.linspace(0.0, 1.0, 1000)
    y = np.sin(2*np.pi * x/L)

    plt.plot(x, y, label=f'L={L}')
    
    plt.xlabel('x')
    plt.xlabel('y')
    plt.legend()
    plt.title('Sine waves')
    plt.show()
# Change wavelength
wave(L=0.5)
../_images/b99387a0b948b47823632e46c28074c27321ce4db6bb3aafc7ce321d70819c39.png

Interact with waves#

  • By adding @widgets.interact(parameters=(init,final)) to our functions we can interactively parameters in the function using slider widgets.

interactive(wave, L=(0.1, 2))

Traveling, standing waves and wave interference#

def wavef2(k=10, t=0, phi=0, v=1):

    x     = np.linspace(0, 1., 1000)
    
    wave1 = np.sin(k*(x-v*t)) 
    wave2 = np.sin(k*(x-v*t)+phi)    #try flipping the direction of velocity to get standing wave
    
    plt.plot(x, wave1,   lw=2, color='blue')
    plt.plot(x, wave2,   lw=2,  color='green')
    plt.plot(x, wave1+wave2, lw=3,  color='red')
    
    plt.ylim([-2.5, 2.5])
    plt.legend(['Wave1','Wave2','Wave1+Wave2'])
    plt.grid('on')
    plt.show()
interactive(wavef2, k=(2, 20), t=(0,50.0,0.1), phi=(0, 2*np.pi, np.pi/8),v=1)

Traveling wave as a function of time and position#

import plotly.express as px

def wave_x_t(A = 1, k = 1.0, omega = 1, phi = 0):
    
    # Create a grid of x and t values
    x = np.linspace(0, 2 * np.pi, 50)
    t = np.linspace(0, 2 * np.pi, 50)
    X, T = np.meshgrid(x, t)

    # Calculate the wave amplitude for each combination of x and t
    Y = A * np.sin(k * X - omega * T + phi)

    return px.scatter_3d(x=X.ravel(), 
              y=T.ravel(), 
              z=Y.ravel(), 
              color = Y.ravel(),
              labels={'x': 'Position', 'y': 'Time', 'z': 'Amplitude'},
              width=1000, 
              height=1000)
wave_x_t(A=1, k = 1.0, omega=2, phi=0)

Normal modes of 1D guitar string#

def wavef(n=1):
    
    L=1
    
    x = np.linspace(0, +1., 1000)
    
    y = np.sin(n*np.pi * x/L)
    
    plt.plot(x, y, lw=3)
    plt.title(label=f'Normal mode # {n}')
    plt.grid('--')
    plt.show()
interactive(wavef, n=(1,20))

1D guitar vibrations as linear combination of normal modes#

For simplicity we will combine two modes with two different mode numbers and shifted with resepct to each ophter by a phase \(\phi\).

\[Mode_1 = cos(\omega t) \cdot sin(n_1\pi \cdot \frac{x}{L})\]
\[Mode_2 = cos(\omega t+\phi) \cdot sin(n_2\pi \cdot \frac{x}{L})\]
def wavef(n1=1, n2=1, phi=0,t=0):
    
    L=1
    omega=1
     
    x = np.linspace(0, +1., 50)
    
    mode1 = np.cos(omega*t) * np.sin(n1*np.pi * x/L)
    
    mode2 = np.cos(omega*t + phi) * np.sin(n2*np.pi * x/L)
    
    plt.plot(x, mode1+mode2, lw=5, color='orange')
    plt.ylim([-2.5, 2.5])
    plt.grid('on')
    plt.show()
interactive(wavef, n1=(1,5), n2=(1,5), phi=(0,2*np.pi), t=(0,100,0.1) )

Normal modes of 2d membrane#

@interact(n=(1,10),m=(1,10))
def membrane(n=1, m=1): 
    
    # Constants
    Lx, Ly = 1.0, 1.0  # Dimensions of the rectangular region
    c = 1.0            # Wave speed
    
    # Create a spatial grid
    Nx, Ny = 100, 100
    x, y  = np.linspace(0, Lx, Nx), np.linspace(0, Ly, Ny)
    X, Y  = np.meshgrid(x, y)

    # Compute spatial part of the normal mode
    spatial_part = np.sin(m * np.pi * X / Lx) * np.sin(n * np.pi * Y / Ly)
   
    fig, ax =plt.subplots()
    ax.contourf(X, Y, spatial_part, 40, cmap='RdBu')

Vibrations of square 2D membrane as a linear combination of normal modes#

def membrane(n1=1, m1=1, n2=1, m2=1, t=0): 
    
    omega=1
    
    L=1                       # size of membrane
    N=40                      # number of grid points along X and Y
    
    x = np.linspace(0,L,N)
    y = np.linspace(0,L,N)
    X,Y = np.meshgrid(x,y)   # create 2D mesh of points along X and Y
     
    mode1 = np.cos(omega*t) * np.sin(n1*np.pi*X/L) * np.sin(m1*np.pi*Y/L) 
     
    mode2 = np.cos(omega*t) * np.sin(n2*np.pi*X/L) * np.sin(m2*np.pi*Y/L) 
   
        
    fig, ax = plt.subplots(figsize=(9,6))            
    ax      = plt.axes(projection='3d')   # Making a 3D plot     
   
    ax.set_zlim([-2.0, 2.0])
    ax.plot_surface(X, Y, mode1+mode2, cmap='RdYlBu') #Do the Plot 
    plt.show()
interactive(membrane, n1=(1,10), m1=(1,10), n2=(1,10), m2=(1,10), t=(0,100))
import holoviews as hv
from holoviews import opts
hv.extension('plotly')

def wave_mode(n, m):
  '''Function to compute the 2D mode shapes'''

  L = 1.0  # Side length
  x, y = np.linspace(0, L, 100), np.linspace(0, L, 100)
  X, Y = np.meshgrid(x, y)

  mode_xy = np.sin(n * np.pi / L * X) * np.sin(m * np.pi / L * Y)

  return hv.Surface(mode_xy)

# Create a HoloMap containing the mode shapes
holomap = hv.HoloMap({(n, m): wave_mode(n, m) for n in range(1,10) for m in range(1,10)}, kdims=['n', 'm'])

holomap.opts(opts.Surface(cmap='viridis', colorbar=True, width=900, height=900))